package com.yaendrel.nativity;

import java.io.*;
import java.text.DateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;

public class MainActivity extends Activity {
    
	Button btnNative, btnDalvik, btnBateriaProcFloat, btnDecaimentoBateria;
	RadioGroup radioNativeDalvik;

	
	// número de execuções no experimento da bateria com processamento float
	int numExecs = 0;
	
	// dateformat pra formatar os timestamps do log de decaimento
	DateFormat df;
	
	private BroadcastReceiver bcrBatteryMonitor = new BroadcastReceiver(){
		
		@Override
		public void onReceive(Context context, Intent intent) {
			// obtem o nível da bateria
			int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
			int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
			int temp = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1);
			int voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1);
			
			// escreve o arquivo csv
			// *lembrar de alterar o nome do arquivo
			String linha = 
				df.format(new Date()) +	"," +
				String.valueOf(level) +	"," +
				String.valueOf(scale) +	"," +
				String.valueOf(temp) + 	"," +
				String.valueOf(voltage) + "\n";
			escreveArquivo("bateria.decaimento.txt", linha);
			Log.v("bateriaLog", linha);
		}
	};
	
	// declarando e inicializando interface com código nativo
	public native int nativeMethod();
	public native int nativeMemory();
	public native int nativeProcInt(int k);
	public native int nativeProcFloat(int k);
	public native int nativeDisk();
	static {
		System.loadLibrary("nativetest");
	}
	
	public void escreveArquivo(String nomeArquivo, String linha) {
		File arquivoBateria = new File(Environment.getExternalStorageDirectory(), nomeArquivo);
    	try {
			FileWriter fw = new FileWriter(arquivoBateria, true); // append = true
			fw.write(linha);
			fw.flush();
			fw.close();
		} catch (Exception e) {
			Toast.makeText(MainActivity.this,
					"Exception escrevendo arquivo: "+e.getMessage(),
					Toast.LENGTH_LONG).show();
		}
	}
	
	// chamado quando a activity é criada
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        btnNative = (Button) findViewById(R.id.ButtonNative);
        btnDalvik = (Button) findViewById(R.id.ButtonDalvik);
        btnBateriaProcFloat = (Button) findViewById(R.id.ButtonBateriaProc);
        btnDecaimentoBateria = (Button) findViewById(R.id.ButtonDecaimentoBateria);
        radioNativeDalvik = (RadioGroup) findViewById(R.id.radioNativeDalvik);
        
        btnNative.setOnClickListener(buttonsListener);
        btnDalvik.setOnClickListener(buttonsListener);
        btnBateriaProcFloat.setOnClickListener(buttonsListener);
        btnDecaimentoBateria.setOnClickListener(buttonsListener);
        
        df = DateFormat.getTimeInstance(DateFormat.LONG);
    }
    
    protected int dalvikMethod() {
    	return 1;
    }

    private int fib(int n) {
    	if (n < 2) return n;
    	else return fib(n - 1) + fib(n - 2);
    }
    
    private int dalvikMemory() {
    	char[][] A = new char[1000][1000];
    	for (int i = 0; i < 1000; ++i)
    		for (int j = 0; j < 1000; ++j)
    			A[i][j] = 'a';
    	return A[999][999];
    }
    
    private int dalvikProcInt(int k) {
    	int i = 0, j = 0;
    	while (i < 2000000+k) {
    		j = i+(i-k);
    		i = j-(i+k);
    		i++;
    	}
    	return i+k;
    }

    private int dalvikProcFloat(int k) {
    	float i = 0, j = 1;
    	while (i < 2000000+k) {
    		j = i*(j+k);
    		j = j/(i-k);
    		i++;
    	}
    	return (int)i+k;
    }

    private int dalvikDisk() {
    	File sdroot = Environment.getExternalStorageDirectory();
    	File teste = new File(sdroot, "teste.dalvik.txt");
    	try {
			OutputStream os = new FileOutputStream(teste);
			byte[] r = "r".getBytes();
			for (int i = 0; i < 5120; i++) os.write(r);
			os.close();
		} catch (Exception e) {
			Toast.makeText(MainActivity.this,
					"Exception escrevendo arquivo: "+e.getMessage(),
					Toast.LENGTH_LONG).show();
		}
    	return 0;
    }
    
    // anonymous implementation of a OnClickListener
    private OnClickListener buttonsListener = new OnClickListener() {
    	public void onClick(View v) {
    		if (v == btnNative) {
    			// run Native method
    			
    			// selected item
    			RadioButton rb = (RadioButton) findViewById(radioNativeDalvik.getCheckedRadioButtonId());
    			
    			if (rb.equals(findViewById(R.id.radioItemLatency))) { // latency
	    			long r = System.nanoTime();
	    			int f = nativeMethod();
	    			Toast.makeText(MainActivity.this,
	    					"Native method run: \n"+
	    					(System.nanoTime()-r) + "ns  \n" +
	    					((System.nanoTime()-r)/1000000) + "ms \n" +
	    					"Value: "+f, Toast.LENGTH_LONG).show();
    			} else if (rb.equals(findViewById(R.id.radioItemMemory))) {
	    			long r = System.nanoTime();
	    			int f = nativeMemory();
	    			Toast.makeText(MainActivity.this,
	    					"Native method run: \n"+
	    					(System.nanoTime()-r) + "ns  \n" +
	    					((System.nanoTime()-r)/1000000) + "ms \n" +
	    					"Value: "+f, Toast.LENGTH_LONG).show();
    			} else if (rb.equals(findViewById(R.id.radioItemProcFloat))) {
	    			long r = System.nanoTime();
	    			int f = nativeProcFloat(0);
	    			Toast.makeText(MainActivity.this,
	    					"Native method run: \n"+
	    					(System.nanoTime()-r) + "ns  \n" +
	    					((System.nanoTime()-r)/1000000) + "ms \n" +
	    					"Value: "+f, Toast.LENGTH_LONG).show();
    			} else if (rb.equals(findViewById(R.id.radioItemProcInt))) {
	    			long r = System.nanoTime();
	    			int f = nativeProcInt(0);
	    			Toast.makeText(MainActivity.this,
	    					"Native method run: \n"+
	    					(System.nanoTime()-r) + "ns  \n" +
	    					((System.nanoTime()-r)/1000000) + "ms \n" +
	    					"Value: "+f, Toast.LENGTH_LONG).show();
    			} else if (rb.equals(findViewById(R.id.radioItemWrite))) {
	    			long r = System.nanoTime();
	    			int f = nativeDisk();
	    			Toast.makeText(MainActivity.this,
	    					"Native method run: \n"+
	    					(System.nanoTime()-r) + "ns  \n" +
	    					((System.nanoTime()-r)/1000000) + "ms \n" +
	    					"Value: "+f, Toast.LENGTH_LONG).show();
    			}
    		} else if (v == btnDalvik) {
    			// run Dalvik method
    			
    			// selected item
    			RadioButton rb = (RadioButton) findViewById(radioNativeDalvik.getCheckedRadioButtonId());
    			
    			if (rb.equals(findViewById(R.id.radioItemLatency))) { // latency
	    			long r = System.nanoTime();
	    			int f = dalvikMethod();
	    			Toast.makeText(MainActivity.this,
	    					"Dalvik method run: \n"+
	    					(System.nanoTime()-r) + "ns  \n" +
	    					((System.nanoTime()-r)/1000000) + "ms \n" +
	    					"Value: "+f, Toast.LENGTH_LONG).show();
    			} else if (rb.equals(findViewById(R.id.radioItemMemory))) {
	    			long r = System.nanoTime();
	    			int f = dalvikMemory();
	    			Toast.makeText(MainActivity.this,
	    					"Dalvik method run: \n"+
	    					(System.nanoTime()-r) + "ns  \n" +
	    					((System.nanoTime()-r)/1000000) + "ms \n" +
	    					"Value: "+f, Toast.LENGTH_LONG).show();
    			} else if (rb.equals(findViewById(R.id.radioItemProcFloat))) {
	    			long r = System.nanoTime();
	    			int f = dalvikProcFloat(0);
	    			Toast.makeText(MainActivity.this,
	    					"Dalvik method run: \n"+
	    					(System.nanoTime()-r) + "ns  \n" +
	    					((System.nanoTime()-r)/1000000) + "ms \n" +
	    					"Value: "+f, Toast.LENGTH_LONG).show();
    			} else if (rb.equals(findViewById(R.id.radioItemProcInt))) {
	    			long r = System.nanoTime();
	    			int f = dalvikProcInt(0);
	    			Toast.makeText(MainActivity.this,
	    					"Dalvik method run: \n"+
	    					(System.nanoTime()-r) + "ns  \n" +
	    					((System.nanoTime()-r)/1000000) + "ms \n" +
	    					"Value: "+f, Toast.LENGTH_LONG).show();
    			} else if (rb.equals(findViewById(R.id.radioItemWrite))) {
	    			long r = System.nanoTime();
	    			int f = dalvikDisk();
	    			Toast.makeText(MainActivity.this,
	    					"Dalvik method run: \n"+
	    					(System.nanoTime()-r) + "ns  \n" +
	    					((System.nanoTime()-r)/1000000) + "ms \n" +
	    					"Value: "+f, Toast.LENGTH_LONG).show();
    			}

    		} else if (v == btnBateriaProcFloat) {
    			// executa experimento da bateria

    			AlertDialog.Builder dialogInicioBuilder = new AlertDialog.Builder(MainActivity.this);
    			dialogInicioBuilder.setMessage("Experimento iniciado");
    			dialogInicioBuilder.setNeutralButton("OK", new DialogInterface.OnClickListener() {
    				public void onClick(DialogInterface dialog, int which) {
    				}
    			});
				final AlertDialog dialogInicio = dialogInicioBuilder.create();
				
				dialogInicio.show();
				
				// inicia a task do benchmark
				final TimerTask benchTask = new TimerTask() {
					public void run() {
						Log.i("benchTask","nativeProcFloat: "+nativeProcFloat(0));
						numExecs++;	
					}
				};
				Timer benchTimer = new Timer();
				benchTimer.schedule(benchTask, 0, 1);
				
				// finaliza a asynctask em 10 minutos (600 000ms)
				TimerTask taskFinalizar = new TimerTask(){
					public void run() {
						benchTask.cancel();
						Log.v("taskFinalizar", "task finalizada. numExecs: "+numExecs);
						numExecs = 0;
						// faz um beep
						ToneGenerator tg = new ToneGenerator(AudioManager.STREAM_SYSTEM, 100);
						tg.startTone(ToneGenerator.TONE_PROP_BEEP2);
					}
				};
				Timer timerFinalizar = new Timer();
				timerFinalizar.schedule(taskFinalizar, 600000);

    		} else if (v == btnDecaimentoBateria) {
    			// inicia o broadcastlistener que monitora bateria
    	        MainActivity.this.registerReceiver(MainActivity.this.bcrBatteryMonitor, 
    	        	    new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
    	        
    	        // escreve timestamp de início
    	        escreveArquivo("bateria.decaimento.txt", 
    	        		"Experimento iniciado: "+ df.format(new Date()));
    			
    		}
    	}
    };
    
    
}